package com.agentai.framework.common.redission;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.codec.JsonJacksonCodec;
import org.redisson.config.ClusterServersConfig;
import org.redisson.config.Config;
import org.redisson.config.ReadMode;
import org.redisson.config.SingleServerConfig;
import org.redisson.spring.starter.RedissonAutoConfigurationCustomizer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.TimeZone;

/**
 * redis配置
 *
 */
@Slf4j
@Configuration
@EnableCaching
@EnableConfigurationProperties(RedisProperties.class)
public class RedisConfiguration {

    @Value("${spring.data.redis.mode}")
    private String redisMode;

    @Resource
    private RedissonProperties redissonProperties;

    @Resource
    private ObjectMapper objectMapper;

    private final RedisProperties redisProperties;

    private static final String REDIS_PROTOCOL_PREFIX = "redis://";

    public RedisConfiguration(RedisProperties redisProperties) {
        this.redisProperties = redisProperties;
    }

    @Bean
    public RedissonClient redissonClient(@Autowired(required = false) List<RedissonAutoConfigurationCustomizer> redissonAutoConfigurationCustomizers) {
        Config config = new Config();
        JavaTimeModule javaTimeModule = new JavaTimeModule();
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(formatter));
        javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(formatter));
        objectMapper.registerModule(javaTimeModule);
        objectMapper.setTimeZone(TimeZone.getDefault());
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
//        // 指定序列化输入的类型，类必须是非final修饰的。序列化时将对象全类名一起保存下来
//        objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL);
//        config.setCheckLockSyncedSlaves(false);#为false性能好，但是数据一致性无法保证,谨慎开启
        config.setThreads(redissonProperties.getThreads())
                .setNettyThreads(redissonProperties.getNettyThreads())
                .setCodec(new JsonJacksonCodec(objectMapper));
        int max = redisProperties.getLettuce().getPool().getMaxActive();
        int min = redisProperties.getLettuce().getPool().getMinIdle();
        switch (redisMode) {
            case "single" -> {
                SingleServerConfig singleConfig = config.useSingleServer()
                        .setAddress(REDIS_PROTOCOL_PREFIX + redisProperties.getHost() + ":" + redisProperties.getPort())
                        .setDatabase(redisProperties.getDatabase())
                        .setPassword(redisProperties.getPassword());
                if (redisProperties.getConnectTimeout() != null) {
                    singleConfig.setConnectTimeout((int) redisProperties.getConnectTimeout().toMillis());
                }
                if (redisProperties.getTimeout() != null) {
                    singleConfig.setTimeout((int) redisProperties.getTimeout().toMillis());
                }
                singleConfig.setConnectionPoolSize(max);
                singleConfig.setConnectionMinimumIdleSize(min);
            }
            case "cluster" -> {
                // 集群配置方式 参考下方注释
                RedissonProperties.ClusterServersConfig cluster = redissonProperties.getClusterServersConfig();

                String[] nodes = convert(cluster.getNodeAddresses());
                ClusterServersConfig clusterConfig = config.useClusterServers()
                        .addNodeAddress(nodes)
                        .setPassword(redisProperties.getPassword());

                clusterConfig.setIdleConnectionTimeout(cluster.getIdleConnectionTimeout());
                clusterConfig.setConnectTimeout(cluster.getConnectTimeout());
                clusterConfig.setTimeout(cluster.getTimeout());
                clusterConfig.setRetryAttempts(cluster.getRetryAttempts());
                clusterConfig.setRetryInterval(cluster.getRetryInterval());
                clusterConfig.setClientName(StrUtil.isBlank(cluster.getClientName()) ? "" : cluster.getClientName());
                clusterConfig.setSubscriptionConnectionPoolSize(cluster.getSubscriptionConnectionPoolSize());
                clusterConfig.setSlaveConnectionMinimumIdleSize(cluster.getSlaveConnectionMinimumIdleSize());
                clusterConfig.setSlaveConnectionPoolSize(cluster.getSlaveConnectionPoolSize());
                clusterConfig.setMasterConnectionMinimumIdleSize(cluster.getMasterConnectionMinimumIdleSize());
                clusterConfig.setMasterConnectionPoolSize(cluster.getMasterConnectionPoolSize());
                clusterConfig.setReadMode(cluster.getReadMode());
                clusterConfig.setSubscriptionMode(cluster.getSubscriptionMode());
                clusterConfig.setScanInterval(cluster.getScanInterval());
                clusterConfig.setKeepAlive(cluster.isKeepAlive());
                clusterConfig.setTcpNoDelay(cluster.isTcpNoDelay());
            }
            default -> throw new IllegalArgumentException("无效的redis mode配置");
        }
        if (redissonAutoConfigurationCustomizers != null) {
            for (RedissonAutoConfigurationCustomizer customizer : redissonAutoConfigurationCustomizers) {
                customizer.customize(config);
            }
        }
        log.info("初始化 redis 配置");
        return Redisson.create(config);
    }


    private String[] convert(List<String> nodesObject) {
        List<String> nodes = new ArrayList<>(nodesObject.size());
        for (String node : nodesObject) {
            if (!node.startsWith(REDIS_PROTOCOL_PREFIX)) {
                nodes.add(REDIS_PROTOCOL_PREFIX + node);
            } else {
                nodes.add(node);
            }
        }
        return nodes.toArray(new String[0]);
    }


}
